#ifndef APP_NTP_H
#define APP_NTP_H

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <unistd.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <endian.h>
#include <sys/time.h>
#include <unistd.h>

/* 32位时间戳 */
struct s_fixedpt {
    uint16_t    intpart;
    uint16_t    fracpart;
};

/* 64位时间戳 */
struct l_fixedpt {
    uint32_t    intpart;
    uint32_t    fracpart;
};

struct ntphdr {
#if __BYTE_ORDER == __BID_ENDIAN
    unsigned int        ntp_li:2;
    unsigned int        ntp_vn:3;
    unsigned int        ntp_mode:3;
#endif
#if __BYTE_ORDER == __LITTLE_ENDIAN
    unsigned int        ntp_mode:3;
    unsigned int        ntp_vn:3;
    unsigned int        ntp_li:2;
#endif
    uint8_t             ntp_stratum;
    uint8_t             ntp_poll;
    int8_t              ntp_precision;
    struct s_fixedpt    ntp_rtdelay;
    struct s_fixedpt    ntp_rtdispersion;
    uint32_t            ntp_refid;
    struct l_fixedpt    ntp_refts;
    struct l_fixedpt    ntp_orits;
    struct l_fixedpt    ntp_recvts;
    struct l_fixedpt    ntp_transts;
};

#define NTP_BUFSIZE         1500
#define NTP_PORT            123
#define NTP_SERVER_NAME     "time.pool.aliyun.com"

/*
 *  用于64位NTP时间戳，即小数部分为32位
 *      NTP_CONV_FRAC32(x)将x转换为NTP时间戳中的小数部分值；
 *      NTP_REVE_FRAC32(x)则相反，将NTP时间戳小数部分值x解析成具体值。
 */
#define NTP_CONV_FRAC32(x)  (uint64_t) ((x) * ((uint64_t)1<<32))    
#define NTP_REVE_FRAC32(x)  ((double) ((double) (x) / ((uint64_t)1<<32)))   


/*
 *  用于32位NTP时间戳，即小数部分为16位
 *      NTP_CONV_FRAC16(x)将x转换为NTP时间戳中的小数部分值；
 *      NTP_REVE_FRAC16(x)则相反，将NTP时间戳小数部分值x解析成具体值。
 */
#define NTP_CONV_FRAC16(x)  (uint32_t) ((x) * ((uint32_t)1<<16))    
#define NTP_REVE_FRAC16(x)  ((double)((double) (x) / ((uint32_t)1<<16)))    


/*
 *  timeval结构中tv_usec字段和NTP时间戳小数部分互转
 */
#define USEC2FRAC(x)        ((uint32_t) NTP_CONV_FRAC32( (x) / 1000000.0 )) 
#define FRAC2USEC(x)        ((uint32_t) NTP_REVE_FRAC32( (x) * 1000000.0 )) 


/*
 *  将l_fixedpt结构（NTP64位时间戳）换算成自1970-01-01所经过的秒数。
 *  该l_fixedpt结构的字段均为网络字节序。
 */
#define NTP_LFIXED2DOUBLE(x)    ((double) ( ntohl(((struct l_fixedpt *) (x))->intpart) - JAN_1970 + FRAC2USEC(ntohl(((struct l_fixedpt *) (x))->fracpart)) / 1000000.0 ))   

/*
 *  获取客户端与服务器的时间偏移量
 *      参数ntp指向服务器应答所在的缓冲区；
 *      参数recvtv指向收到服务器应答的本地时间；
 */
double get_offset(const struct ntphdr *ntp, const struct timeval *recvtv);
/*
 *  获取客户端与服务器之间的往返时延
 *      参数ntp指向服务器应答所在的缓冲区；
 *      参数recvtv指向收到服务器应答的本地时间；
 */
double get_rrt(const struct ntphdr *ntp, const struct timeval *recvtv);
/*
 *  构建一个NTP请求报文
 *      c参数buf指向存放NTP报文的缓冲区；
 *      size为值-结果参数，传入是为缓冲区长度，返回时为NTP报文长度
 */
int get_ntp_packet(void *buf, size_t *size);
void print_ntp(const struct ntphdr *ntp);
in_addr_t inet_host(const char *host);
#endif /* APP_NTP_H */
